Accept type expressions in Parser.addini(type=...)#14751
Accept type expressions in Parser.addini(type=...)#14751Pierre-Sassoulas wants to merge 8 commits into
Conversation
d9a3d92 to
412e21b
Compare
bluetech
left a comment
There was a problem hiding this comment.
Thanks, LGTM!
(As I mentioned in the previous comment, I also think Literal support would be nice for some configs, but that can be done separately).
| plain Python type. | ||
| """ | ||
| if isinstance(type_, str) and type_ in _INI_TYPE_TAGS: | ||
| return cast("_IniTypeTag", type_) |
There was a problem hiding this comment.
Is the string type needed here (i.e. does replacing with _IniTypeTag not work)?
| FILE_OR_DIR = "file_or_dir" | ||
|
|
||
| #: The string tags accepted by :meth:`Parser.addini` for its ``type`` argument. | ||
| _IniTypeTag = Literal[ |
There was a problem hiding this comment.
Let's add : TypeAlias to these, then it will be automatically upgraded to type statement once that's available.
| if default is NOTSET: | ||
| default = get_ini_default_for_type(type) | ||
| if isinstance(ini_type, tuple): | ||
| # A union has no unambiguous implicit default; require an |
There was a problem hiding this comment.
I think the exception message is clear enough that the comment is redundant.
|
I think this is a simple and clear enough extension to the existing mechanism that even if we ever go with something more elaborate like @RonnyPfannschmidt proposed, it won't be a bother. |
| type: _IniTypeTag | ||
| | type[bool | int | float | str] | ||
| | types.UnionType |
There was a problem hiding this comment.
I think this can be written more type-safely as
type: _IniTypeTag | TypeForm[bool | int | float | str] | None = None,where TypeForm is imported from typing_extensions under if TYPE_CHECKING (added in Python 3.15). It seems to work as intended in mypy at least.
|
It's not blocking my ever evading elaboration 😅 |
A Literal of strings restricts an ini option's value to the given choices, for example type=Literal["auto", "long"]. The choices are shown in the --help output. Since the choices have no unambiguous implicit default, an explicit default is required, as for unions. Suggested by bluetech in the review of pytest-dev#14751. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
412e21b to
6c3ff25
Compare
A Literal of strings restricts an ini option's value to the given choices, for example type=Literal["auto", "long"]. The choices are shown in the --help output. Since the choices have no unambiguous implicit default, an explicit default is required, as for unions. Suggested by bluetech in the review of pytest-dev#14751. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
47369e5 to
91dc674
Compare
The Literal of ini option type tags was repeated inline in Parser.addini and get_ini_default_for_type. Name it, and name the full form the type argument accepts, ahead of extending it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
addini now accepts plain Python types (str, bool, int, float) and unions of them (e.g. `int | str`) for its `type` argument, in addition to the existing string tags, as proposed by bluetech in pytest-dev#14675. A union means the option accepts a value of any of its member types: getini tries each member in order and returns the first that accepts the value. In TOML config the native value may be of any member type, and string-based formats (INI files, -o overrides) coerce it to the first member that accepts it. Internally union types are normalized to a tuple of the existing string tags, reusing the per-tag coercion; single-tag registration and lookup are unchanged. An invalid `type` argument raises ValueError instead of asserting, since it is user (plugin) provided, and registering a union type without an explicit default also raises ValueError instead of silently defaulting to None. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Annotate the ini type aliases with TypeAlias
- Type addini(type=...) with TypeForm instead of type[...] | UnionType,
which statically rejects unions of unsupported types
- Drop the unneeded string quoting in cast("_IniTypeTag", ...)
- Remove a comment redundant with its exception message
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Literal of strings restricts an ini option's value to the given choices, for example type=Literal["auto", "long"]. The choices are shown in the --help output. Since the choices have no unambiguous implicit default, an explicit default is required, as for unions. Suggested by bluetech in the review of pytest-dev#14751. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Bruno Oliveira <bruno@soliv.dev>
Apply simplification review findings: - Merge the two tag lookup tables in _ini_type_to_tag into one dict and a try/except, dropping the isinstance guards and the cast - Compute get_origin(type) once in addini - Add _ini_type_repr and use it for --help output and the getini error messages, instead of three inline formatting variants (Literal choices are now consistently rendered 'auto' | 'long') - Move the Literal branch from _getini into _getini_value so type interpretation happens in one layer - Merge the two versionadded 9.2 blocks in the addini docstring and drop the redundant second changelog sentence - Tests: hoist the duplicated conftests to class constants, parametrize the invalid-type and Literal cases, and merge the two --help tests into one Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57ce0f0 to
7f54d20
Compare
|
Tried to cleanup the history while keeping it reviewable following the comments, I think it's ready now. I made the Litteral change I think it makes more sense to do everything at once. |
|
Thanks for adding the Literal support! I haven't reviewed that part yet, but just from a quick look, I think it doesn't support e.g. Ideally, the support would be for any recursive combination of However the current support in the PR is fine and above compatible with it don't have to implement it in this PR. |
The alias only exists under TYPE_CHECKING, so Sphinx cannot resolve the reference in the rendered Parser.addini signature, failing the docs build (-W with nitpicky). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
There isn't a lot of additional work to do pytest/src/_pytest/config/__init__.py Lines 2038 to 2051 in a53a4d9 |
|
Although for backward compat we will have to keep the |
Allows types such as int | Literal["auto"], where a value is accepted if any member accepts it. This is the shape of several existing options that take a number or a special string sentinel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Do we still need it for the future ? I pushed what I had. Imo Litteral is better than str in the case it can be used. |
Prior refactor in order to be able to do #14692 cleanly.
addini now accepts plain Python types (str, bool, int, float) and unions of them (e.g.
int | str) for itstypeargument, in addition to the existing string tags, as proposed by bluetech in #14675.